#!/usr/bin/env bash
set -euo pipefail
if ! command -v buildah >/dev/null; then
    echo "buildah does not exist but is required for running this script"
    exit 1
fi

# Versions to be used
# shellcheck source=versions
source "$(dirname "${BASH_SOURCE[0]}")"/versions

# Commonly used constants
ARCH_AMD64=amd64

# The default values
TARGET_IMAGE=crio-build
REGISTRY=
ARCH=$ARCH_AMD64
GO=1.15
BASE_IMAGE=docker.io/golang
TAG=latest
DRYRUN=false
WORK_CTR=

usage() {
    printf "Usage: %s -a GO_ARCH -g GO_VERSION [ -r REGISTRY ] [ -t IMAGE ] [ -v TAG ] [ -d ] [ -h ]\n\n" \
        "$(basename "$0")"
    echo "Possible arguments:"
    printf "  -a\ttarget architecture of the image (default: '%s')\n" $ARCH
    printf "  -d\tdon’t build the image, only print the resulting image name and exit\n"
    printf "  -g\tgo version which should be used (default: '%s')\n" $GO
    printf "  -r\tregistry to be added to the target image (default: '%s')\n" $REGISTRY
    printf "  -t\ttarget image name (default: '%s-%s-go%s')\n" $TARGET_IMAGE $ARCH $GO
    printf "  -v\timage version tag to be used (default: '%s')\n" $TAG
    printf "  -h\tShow this help message\n"
}

parse_args() {
    while getopts 'a:g:r:t:v:w:dh' OPTION; do
        case "$OPTION" in
        a)
            ARCH="$OPTARG"
            ;;
        d)
            DRYRUN=true
            ;;
        g)
            GO="$OPTARG"
            ;;
        r)
            REGISTRY="$OPTARG"
            ;;
        t)
            TARGET_IMAGE="$OPTARG"
            ;;
        v)
            TAG="$OPTARG"
            ;;
        w)
            WORK_CTR="$OPTARG"
            ;;
        h)
            usage
            exit 0
            ;;
        ?)
            usage
            exit 1
            ;;
        esac
    done

    TARGET_IMAGE=$TARGET_IMAGE-$ARCH-go$GO:$TAG
    if [[ $REGISTRY != "" ]]; then
        TARGET_IMAGE=$REGISTRY/$TARGET_IMAGE
    fi

    if [[ "$DRYRUN" == true ]]; then
        echo -n "$TARGET_IMAGE"
        exit 0
    fi
}

digest() {
    IMAGE=$BASE_IMAGE:$GO
    buildah rmi "$IMAGE" 2>/dev/null || true
    buildah manifest inspect docker://"$IMAGE" |
        jq -er "[.manifests[] | select(
                 .platform.architecture == \"$ARCH\" and
                 .platform.os == \"linux\")][0].digest"
}

unshare() {
    echo "Building target image: $TARGET_IMAGE"
    BASE_IMAGE=$BASE_IMAGE@$(digest)
    echo "Using base image: $BASE_IMAGE"

    WORK_CTR=$(buildah from --pull "$BASE_IMAGE")
    buildah unshare "$0" -w "$WORK_CTR" "$@"
    exit 0
}

main() {
    parse_args "$@"

    if [[ $WORK_CTR == "" ]]; then
        unshare "$@"
    fi

    MOUNT_DIR=$(buildah mount "$WORK_CTR")

    set -x
    install_packages
    install_bats
    install_conmon
    install_cri_tools
    install_runc
    install_cni_plugins
    install_files

    buildah unmount "$WORK_CTR"
    buildah config --arch "$ARCH" "$WORK_CTR"
    buildah commit --squash "$WORK_CTR" "$TARGET_IMAGE"
    buildah images "$TARGET_IMAGE"
}

run() {
    buildah run -t "$WORK_CTR" "$@"
}

install_packages() {
    # prepare Google Cloud SDK
    run bash -c 'echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" |
        tee -a /etc/apt/sources.list.d/google-cloud-sdk.list'
    run bash -c 'curl https://packages.cloud.google.com/apt/doc/apt-key.gpg |
        apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -'

    run apt-get update
    run apt-get install -y \
        apparmor \
        autoconf \
        automake \
        bison \
        bsdmainutils \
        btrfs-progs \
        build-essential \
        clang-format \
        conntrack \
        e2fslibs-dev \
        gawk \
        gettext \
        google-cloud-sdk \
        iptables \
        jq \
        libaio-dev \
        libapparmor-dev \
        libbtrfs-dev \
        libcap-dev \
        libdevmapper-dev \
        libdevmapper1.02.1 \
        libfuse-dev \
        libglib2.0-dev \
        libgpgme-dev \
        liblzma-dev \
        libnet1-dev \
        libnl-3-dev \
        libprotobuf-c-dev \
        libprotobuf-dev \
        libseccomp-dev \
        libsystemd-dev \
        libtool \
        libudev-dev \
        netcat-openbsd \
        parallel \
        protobuf-c-compiler \
        protobuf-compiler \
        python-protobuf \
        socat
    run apt-get clean
    rm -rf "$MOUNT_DIR"/var/lib/apt/lists/*
}

install_bats() {
    run git clone https://github.com/bats-core/bats-core
    run bash -c "\
        cd bats-core && \
        git checkout ${VERSIONS["bats"]} && \
        ./install.sh /usr"
    run rm -rf bats-core
    run mkdir -p ~/.parallel
    run touch ~/.parallel/will-cite
}

install_conmon() {
    run git clone https://github.com/containers/conmon
    run bash -c "\
        cd conmon && \
        git checkout ${VERSIONS["conmon"]} && \
        make PREFIX=/ all install"
    run rm -rf conmon
}

install_cri_tools() {
    ARCHIVE=${VERSIONS["cri-tools"]}-linux-$ARCH.tar.gz
    URL=https://github.com/kubernetes-sigs/cri-tools/releases/download

    BINARIES=(crictl critest)
    for BINARY in "${BINARIES[@]}"; do
        TARBALL=$BINARY-$ARCHIVE
        echo "Downloading $TARBALL"
        wget -O "$TARBALL" $URL/"${VERSIONS["cri-tools"]}"/"$TARBALL"
        tar xf "$TARBALL" --no-same-owner -C "$MOUNT_DIR"/usr/bin
        run chown root:root /usr/bin/"$BINARY" # https://circleci.com/docs/2.0/high-uid-error
        run "$BINARY" --version
        rm "$TARBALL"
    done
}

install_cni_plugins() {
    if [[ "$ARCH" != "$ARCH_AMD64" ]]; then
        echo "CNI plugins are not available pre-built for $ARCH"
        return
    fi
    URL=https://github.com/containernetworking/plugins/releases/download
    TARBALL=cni-plugins-linux-$ARCH-${VERSIONS["cni-plugins"]}.tgz
    CNI_DIR="$MOUNT_DIR"/opt/cni/bin
    mkdir -p "$CNI_DIR"
    wget -O "$TARBALL" $URL/"${VERSIONS["cni-plugins"]}"/"$TARBALL"
    tar xf "$TARBALL" -C "$CNI_DIR"
    rm "$TARBALL"
    ls -lah "$CNI_DIR"
}

install_runc() {
    if [[ "$ARCH" != "$ARCH_AMD64" ]]; then
        echo "runc is not available pre-built for $ARCH"
        return
    fi
    URL=https://github.com/opencontainers/runc/releases/download
    BINARY="$MOUNT_DIR"/usr/bin/runc
    wget -O "$BINARY" $URL/"${VERSIONS["runc"]}"/runc."$ARCH"
    chmod +x "$BINARY"
    run runc --version
}

install_files() {
    REPO_ROOT=$(git rev-parse --show-toplevel)
    mkdir -p "$MOUNT_DIR"/etc/containers/registries.d
    cp "$REPO_ROOT"/test/policy.json "$MOUNT_DIR"/etc/containers
    cp "$REPO_ROOT"/test/redhat_sigstore.yaml \
        "$MOUNT_DIR"/etc/containers/registries.d/registry.access.redhat.com.yaml
}

main "$@"
